/*
 * Copyright 2016-2017 TVI Go Easy.
 * Created on 2017/4/20 12:13
 */
package org.mechanic.fund.domain.utils.mapper;

import ma.glasnost.orika.MappingException;
import ma.glasnost.orika.metadata.Type;
import org.mechanic.fund.domain.BaseEntity;
import org.mechanic.utils.collection.ListUtil;
import org.mechanic.utils.mapper.BeanMapper;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.List;

/**
 * DTO与Domain互相转换。
 *
 * @author mechanic
 * @version 0.0.1
 */
public class DtoBeanMapper {

    /**
     * 批量转换 DTO 为 Domian。
     *
     * @param sourceList      需要转换的DTO对象的集合
     * @param sourceType      源对象类型
     * @param destinationType 目标对象类型
     * @param currUserId      当前用户id。该值会写入创建人和更新人字段
     * @return 转换后的Domain对象
     */
    public static <S, D> List<D> mapToDtoList(
      List<S> sourceList,
      Type<S> sourceType,
      Type<D> destinationType,
      String currUserId) {

        if (ListUtil.isEmpty(sourceList)) {
            return null;
        }
        //批量转换 DTO 为 Domain
        List<D> dest = BeanMapper.mapList(sourceList, sourceType, destinationType);

        //为每条记录从BaseEntity继承的公共字段赋值
        for (D o : dest) {
            setValueForBaseFeile(o, currUserId);
        }
        return dest;
    }

    /**
     * 为继承了BaseEntity的Domian对象设置操做信息(创建人、创建时间、更新人、更新时时间)。
     *
     * @param domain     需要设置的对象
     * @param currUserId 当前用户id。该值会写入创建人和更新人字段
     * @return 设置后的对象
     */
    public static <T> T setOperationToDomain(
      T domain,
      String currUserId) {

        if (domain == null) {
            return null;
        }

        //为每条记录从BaseEntity继承的公共字段赋值
        setValueForBaseFeile(domain, currUserId);
        return domain;
    }

    /**
     * 为继承了BaseEntity的Domian对象集合设置操做信息(创建人、创建时间、更新人、更新时时间)。
     *
     * @param domians     需要设置的对象集合
     * @param currUserId 当前用户id。该值会写入创建人和更新人字段
     * @return 设置后的对象
     */
    public static <T> List<T> setOperationToList(
      List<T> domians,
      String currUserId) {

        if (ListUtil.isEmpty(domians)) {
            return null;
        }

        //为每条记录从BaseEntity继承的公共字段赋值
        for (T o : domians) {
            setValueForBaseFeile(o, currUserId);
        }
        return domians;
    }

    /**
     * 为 domain 的从 BaseEntity 继承下来的除 id 以外的其他字段设置值。
     * 会判断传入的 domain 的 id 是否为空，如果为空则表示是新增对象，会为创建人和创建时间赋值。
     *
     * @param domain     继承了 BaseEntity 的实体
     * @param currUserId 当前用户id。该值会写入创建人和更新人字段
     */
    private static <D> void setValueForBaseFeile(D domain, String currUserId) {

        if (domain == null) {
            throw new MappingException("无法为空的对象设置值。");
        }

        //BaseEntity 父类对象中的公共字段名称。
        String createUserFieldName = "createUser";
        String createTimeFieldName = "createTime";
        String updateUserFieldName = "updateUser";
        String updateTimeFieldName = "updateTime";

        //获取父类
        Class<?> baseEntityClass = domain.getClass().getSuperclass();

        if (baseEntityClass == null || baseEntityClass != BaseEntity.class) {
            throw new MappingException("无法为无父类或者父类不正确的对象设置值。");
        }

        LocalDateTime now = LocalDateTime.now();

        try {
            //获取id
            //Field idField = baseEntityClass.getDeclaredField("id");
            //将字段id设为可访问
            //idField.setAccessible(true);
            //获取id的值。
            //String idValue = String.valueOf(idField.get(domain));
            //Object idValue = idField.get(domain);

            //如果id无值，则按新增记录处理，为创建人和创建时间赋值
            //if (idValue == null || idValue.equals("")) {
            //为创建人赋值
            Field createUserField = baseEntityClass.getDeclaredField(createUserFieldName);
            createUserField.setAccessible(true);
            createUserField.set(domain, currUserId);

            //为创建时间赋值
            Field createTimeField = baseEntityClass.getDeclaredField(createTimeFieldName);
            createTimeField.setAccessible(true);
            createTimeField.set(domain, now);
            //}

            //为最后更新人赋值
            Field updateUserField = baseEntityClass.getDeclaredField(updateUserFieldName);
            updateUserField.setAccessible(true);
            updateUserField.set(domain, currUserId);

            //为更新时间赋值
            Field updateTimeField = baseEntityClass.getDeclaredField(updateTimeFieldName);
            updateTimeField.setAccessible(true);
            updateTimeField.set(domain, now);

        } catch (IllegalAccessException | NoSuchFieldException e) {
            throw new RuntimeException("DTO转换为Domain的过程中，为公共字段设置默认值时发生错误。" + e);
        }
    }
}